
#include "Producer.h"

bool Producer::stopFlag = false;

Producer::Producer() {
	this->stat = new Statistics();
	this->id = -1;
	this->container = NULL;
	this->peakLength = Config::peakLength;
	this->timeBetweenPeaks = Config::timeBetweenPeaks;
	this->timeMeasurements = new list<long>();
	this->taskId = 0;
	ts = new timespec();
}


Producer::~Producer(){
	delete stat;
	delete timeMeasurements;
}

/*
	producer run:
	- register producer to container
	- while not stopped - do iterations of the followings:
		peak period: generate and insert to container <peakLength> tasks
		silent period: sleep for a random time
	note that stopping the producer is only possible between iterations.
	This means that once a producer started a peak period, it will always complete it before finishing
*/
void Producer::run(){
	// need to produce first task outside of the loop?
	container->registerProducer(id);
	int iter = 0;
	normalRandPair* nrp = new NormalRandPair();
	
	while(!stopFlag)
	{
		// peak period
		/* sample peak start time */
		clock_gettime(_POSIX_MONOTONIC_CLOCK,ts);
		unsigned long start_ns = ts->tv_nsec;
		unsigned long start_sec = ts->tv_sec;
		
		/* task generation and insertion */
		for(int i = 0; i < peakLength; i++)
		{
			DummyTask* task = createTask(taskId++);
			//task->producerId = id; // debug
			// if i%100 == 0 - set task insertion time to current time in ms
			if(i%100 == 0)
			{
				clock_gettime(_POSIX_MONOTONIC_CLOCK,ts);
				task->insertionTime_sec = ts->tv_sec;
				task->insertionTime_ns = ts->tv_nsec;
			}
			//if(task->getId()%100 != 0 &&(task->insertionTime_sec != 0 || task->insertionTime_ns != 0)){cout << "p error " << task->getId() << " " << task->insertionTime_sec << " " << task->insertionTime_ns << endl;}
			container->put(task,stat);
		}
		
		/* sample peak end time and update timeMeasurements */
		clock_gettime(_POSIX_MONOTONIC_CLOCK,ts);
		unsigned long finish_ns = ts->tv_nsec;
		unsigned long finish_sec = ts->tv_sec;
		if(start_sec < finish_sec)
		{
			finish_ns += 1000000000;
		}
		timeMeasurements->push_front(finish_ns-start_ns);

		// silent period
		/* on even iterations - draw a pair of values from a normal distribution, to be used as sleep times */
		double normalFactor = 0;
		if(iter % 2 == 0)
		{
			normalRand(nrp);
			normalFactor = nrp->x;
		}
		else
		{
			normalFactor = nrp->y;
		}
		long timeToSleep = (long)(timeBetweenPeaks + sqrt(timeBetweenPeaks)*normalFactor);
		if(timeToSleep > 0){usleep(timeToSleep);}
		iter++;
	}
	delete ts;
	delete nrp;
	/************** for debug only *************************/
	/*
	// print producer's timeMeasurements
	list<long>::iterator it;
	for(it = timeMeasurements -> begin(); it != timeMeasurements -> end(); it++)
	{
		cout << "p" << id <<":" << *it << endl;
	}*/
	/******************************************************/
}

/* generate a pair of random values from normal gaussian distribution using Box-Muller transformation */
void Producer::normalRand(NormalRandPair* nrp){
	srand(TaskContainer::generateRandomSeed());
	double a = (double)(rand()%1000 + 1)/1000.0;
	srand(TaskContainer::generateRandomSeed());
	double b = (double)(rand()%1000 + 1)/1000.0;
	double c = sqrt(-2*log(a));
	nrp->x = c*cos(2*PI*b);
	nrp->y = c*sin(2*PI*b);
}


